# -*- coding:utf-8 -*-

import time
import urllib
import urllib2
import md5

#import sys
#import httplib
#import urlparse
#import mimetypes

""" json or xml 会使返回值的数据结构和名称产生很大不同，需分别写不同的函数处理返回的数据 """
RESPONSE_FORMAT = 'json'
try:
    import simplejson
except ImportError:
    try:
        from django.utils import simplejson
    except ImportError:
        from xml.dom import minidom
        RESPONSE_FORMAT = 'xml'

# support Google App Engine urlfetch.
try:
    from google.appengine.api import urlfetch

    def urlread(url, data=None, all=False):
        if data is not None:
            headers = {'Content-type': "application/x-www-form-urlencoded"}
            method = urlfetch.POST
        else:
            headers = {}
            method = urlfetch.GET

        result = urlfetch.fetch(url, method=method, payload=data, headers = headers)
        if result.status_code == 200:
            if not all:
                return result.content
            else:
                return result
        else:
            raise urllib2.URLError("fetch error url=%s, code=%d" % (url, result.status_code))
    
except ImportError:
    def urlread(url, data=None, all=False):
        res = urllib2.urlopen(url, data=data)
        if not all:
            return res.read()
        else:
            return res
    
" constants "
__all__ = ['Sip']
VERSION = '0.1'

ISOTIMEFORMAT= '%Y-%m-%d %X'

SIP_REST_URL = 'http://sip.alisoft.com/sip/rest'
SIP_LOGIN_URL = 'http://sip.alisoft.com/sip/login'
SIP_SERVICE_URL = 'http://sip.alisoft.com/sip/sipService'
TAOBAO_ITEM_URL = 'http://item.taobao.com/auction/item_detail-0db0-'


" namespaces "
NAMESPACES = []

" method "
SIPMETHODS = {
#    """ Sip server """
#    'sip':{
#        'user':{
#            # 查询sip_appkey, sip_apiname, sip_sessionid对应的token的绑定用户
#            'query':[
#                {'sip_service_command': 'sip.user.query'},
#                ('sip_apiname', str, []),
#            ],
#            # 解除sip_appkey, sip_apiname, sip_sessionid对应的token的绑定
#            'unbind':[
#                {'sip_service_command': 'sip.user.unbind'},
#                ('sip_apiname', str, []),
#            ],
#            # 查询sip_appkey, sip_apiname, sip_sessionid是否绑定，若已绑定，则返回绑定的具体信息
#            'isLogin':[
#                {'sip_service_command': 'sip.user.isLogin'},
#                ('sip_apiname', str, []),
#                ('sip_tokentype', str, []), # 绑定的Token类型（request与session两类）
#            ],
#        },
#    },
    # """ TaoBao """
    'taobao': {
        # " 产品API "
        'products':{
            # 搜索产品信息
            'search':[
                {'apiType': 1},
                ('fields', list, []),
                ('q', str, ['optional']), # 搜索的关键词 (q,cid和props必须至少传一个)
                ('cid', str, ['optional']), # 类目ID
                ('props', str, ['optional']), # 属性、属性值的组合，格式：pid:vid;pid:vid
                ('page_size', int, ['optional']),
                ('page_no', int, ['optional']),
            ],
        },
        'product':{
            # 获取一个产品的信息
            'get':[
                {'apiType': 1},
                ('fields', list, []),
                ('cid', str, ['optional']),
                ('props', str, ['optional']),
                ('product_id', str, ['optional']),
            ],
        },
        # " 店铺API "
        'shop':{
            # 获取卖家店铺基本信息
            'get':[
                {'apiType': 3},
                ('fields', list, []),
                ('nick', str, []),
            ],
            # 更新店铺的标题、公告及介绍
            'update':[
                {'apiType': 2},
                ('title', str, ['optional']), # 标题、公告和介绍至少传一个
                ('bulletin', str, ['optional']),
                ('desc', str, ['optional']),
            ],
#            # 获取剩余的橱窗推荐数量
#            'showcase':{
#                'remainCount':[
#                    {'apiType': 2},
#                    ('sip_sessionid', str, []),
#                ],
#            },
        },
        # " 交易API  指已经买下商品的行为，不包括出价记录 "
        'trades':{
            'bought':{
                # 搜索当前会话用户做为买家达成的交易记录。不支持历史交易数据。
                'get':[
                    {'apiType': 2},
                    ('fields', list, []),
                    ('start_created', str, ['optional']),
                    ('page_no', int, ['optional']),
                    ('page_size', int, ['optional']),
                    ('title', str, ['optional']),
                    ('status', str, ['optional']),
                    ('end_created', str, ['optional']),
                ],
            },
            'sold':{
                # 搜索当前会话用户（做为卖家）已卖出的交易数据。不支持历史交易数据。
                'get':[
                    {'apiType': 2},
                    ('fields', list, []),
                    ('start_created', str, ['optional']),
                    ('page_no', int, ['optional']),
                    ('page_size', int, ['optional']),
                    ('title', str, ['optional']),
                    ('status', str, ['optional']),
                    ('end_created', str, ['optional']),
                ],
            },
            # 搜索交易公开信息
            'get':[
                {'apiType': 1},
                ('fields', str, []),
                ('seller_nick', str, []),
                ('iid', str, []),
                ('page_no', int, ['optional']),
                ('page_size', int, ['optional']),
            ],
        },
        'trade':{
            # 获取单条交易信息，包含支付宝交易号、实付金额及邮费
            'get':[
                {'apiType': 2},
                ('fields', list, []), # seller_nick, buyer_nick, title, type, created, sid, buyer_message, tid, seller_rate, buyer_rate, status, payment, alipay_no, pay_time, end_time, out_sid, modified, buyer_obtain_point_fee, buyer_memo, seller_memo, point_fee, real_point_fee, total_fee, orders
                ('tid', str, []),
            ],
        },
        'orders':{
            # 查询某商品的出价记录
            'get':[
                {'apiType': 1},
                ('fields', list, []),
                ('iid', str, []),
                ('seller_nick', str, ['optional']),
                ('page_on', int, ['optional']),
                ('page_size', int, ['optional']),
            ],
        },
        # " 类目属性API   实现淘宝搜索页面的类目属性展示和商品发布类目属性的选择。"
        'itemcats':{
            # 得到后台供卖家发布商品的标准商品类目
            'get':[
                {'apiType': 2},
                ('parent_cid', str, ['optional']), # cids和parent_cid至少传一个。
                ('cids', str, ['optional']),
            ],
            'get':{
                # 得到后台供卖家发布商品的标准商品类目
                'v2':[
                    {'apiType': 2},
                    ('fields', str, []),
                    ('datetime', str, ['optional']), #时间戳，如果没有传，则取所有的类目信息(如果传了parent_cid或者cids，则忽略datetime)
                    ('parent_cid', str, ['optional']),
                    ('cids', str, ['optional']),
                ],
            },
            'list':{
                # 得到前台展示的供买家浏览的商品类目
                'get':[
                    {'apiType': 1},
                    ('parent_cid', str, ['optional']), # cids和parent_cid至少传一个。
                    ('cids', str, ['optional']),
                ],
            },
        },
        'shopcats':{
            'list':{
                # 得到前台展示的店铺类目
                'get':[
                    {'apiType': 1},
                    ('sip_sessionid', str, ['optional']),
                ],
            },
        },
        'sellercats':{
            'list':{
                # 得到前台展示的店铺内卖家自定义商品类目
                'get':[
                    {'apiType': 1},
                    ('nick', str, []),
                ],
            },
        },
        'itemprops':{
            # 后台供卖家发布商品用的商品标准属性和属性值列表
            'get':[
                {'apiType': 2},
                ('cid', str, []),
            ],
            'get':{
                # 获取标准商品类目属性
                'v2':[
                    {'apiType': 1},
                    ('fields', list, []),
                    ('cid', str, []),
                    ('pid', str, ['optional']), # 属性id (取类目属性时，传pid，不用同时传PID和parent_pid)
                    ('parent_pid', str, ['optional']),
                    ('is_key_prop', str, ['optional']),
                    ('is_sale_prop', str, ['optional']),
                    ('datetime', str, ['optional']),
                    ('is_color_prop', bool, ['optional']),
                    ('is_enum_prop', bool, ['optional']),
                    ('is_input_prop', bool, ['optional']),
                ],
            },
            'list':{
                # 得到前台展示的供买家浏览的商品属性
                'get':[
                    {'apiType': 1},
                    ('cid', str, []),
                ],
            },
        },
        'spu':{
            # 得到spu(标准属性单元，可以理解为一种产品)
            'get':[
                {'apiType': 1},
                ('fields', list, []),
                ('cid', str, []),
                ('props', str, []),
            ],
        },
        'itemprop':{
            # 得到后台供卖家发布商品用的单个商品属性
            'get':[
                {'apiType': 2},
                ('cid', str, ['optional']),
                ('pid', str, ['optional']),
                ('child_path', str, ['optional']),
            ],
            'list':{
                # 得到前台展示的供买家浏览的商品属性
                'get':[
                    {'apiType': 1},
                    ('cid', str, ['optional']),
                    ('pid', str, ['optional']),
                    ('child_path', str, ['optional']),
                ],
            },
        },
        'itempropvalues':{
            # 获取标准类目属性值
            'get':[
                {'apiType': 1},
                ('fields', str, []),
                ('cid', str, []),
                ('pvs', str, ['optional']),
                ('datetime', str, ['optional']),
            ],
        },
# todo
        # " 评价API None "
        'traderate':{
        },
        # " 商品API "
        'items': {
            # 搜索商品信息。支持分页和排序。目前该接口得到的数据和真实的商品数据会有１０分钟左右的延迟。
            'get':[
                {'apiType': 1},
                ('fields', list, [('default', ['iid'])]),
                ('q', str, ['optional']), # 搜索字段 q,cid,nicks,props,product_id中至少要一个 
                ('nicks', list, ['optional']),
                ('cid', str, ['optional']),
                ('start_price', int, ['optional']),
                ('end_price', int, ['optional']),
                ('page_no', int, ['optional']),
                ('page_size', int, ['optional']),
                ('order_by', str, ['optional']),
                ('props', str, ['optional']),
                ('product_id', str, ['optional']),
                ('ww_status', bool, ['optional']),
                ('post_free', bool, ['optional']),
#                ('location.city', str, ['optional']),
                {'location': [
                    ('state', str, ['optional']),
                    ('city', str, ['optional']),
                    ],
                },
            ],
            'onsale': {
                # 搜索当前会话用户（必须为卖家）的库存商品。支持分页。
                'get': [
                    {'apiType': 2},
                    ('fields', list, [('default', ['iid'])]),
                    ('q', str, ['optional']),
                    ('page_no', int, ['optional']),
                    ('page_size', int, ['optional']),
                    ('banner', str, ['optional']),
                    ('order_by', str, ['optional']),
                ],
            },
            'instock': {
                # 搜索当前会话用户（必须为卖家）的库存商品。支持分页。
                'get': [
                    {'apiType': 2},
                    ('fields', list, [('default', ['iid'])]),
                    ('q', str, ['optional']),
                    ('page_no', int, ['optional']),
                    ('page_size', int, ['optional']),
                    ('banner', str, ['optional']),
                    ('order_by', str, ['optional']),
                ],
            },
        },
        'item':{
            # 得到单个商品信息。
            'get': [
                {'apiType': 3},
                ('iid', str, []),
                ('nick', str, []),
                ('fields', list, [('default', ['iid'])]),
            ],
            # 更新商品信息。目前暂不支持对二手宝贝进行更新。
            'update': [
                {'apiType': 2},
                ('iid', str, []),
                ('approve_status', str, ['optional']),
                ('cid', str, ['optional']),
                ('num', int, ['optional']),
                ('price', str, ['optional']),
                ('title', str, ['optional']),
                ('desc', str, ['optional']),
                ('valid_thru', str, ['optional']),
                ('has_invoice', bool, ['optional']),
                ('has_warranty', bool, ['optional']),
                ('auto_repost', bool, ['optional']),
                ('has_showcase', bool, ['optional']),
                ('post_fee', str, ['optional']),
                ('express_fee', str, ['optional']),
                ('ems_fee', str, ['optional']),
                ('list_time', str, ['optional']),
                ('freight_payer', str, ['optional']),
                ('seller_cids', str, ['optional']), # 店铺类目列表.这里只做添加操作，不会覆盖原有的分类.
                ('has_discount', str, ['optional']),
                ('increment', bool, ['optional']),
                ('image', str, ['optional']),
                ('auction_point', str, ['optional']),
                ('property_alias', str, ['optional']),
                ('sku_ids', str, ['optional']),
                ('sku_properties', str, ['optional']),
                ('sku_quantities', str, ['optional']),
                ('sku_prices', str, ['optional']),
                ('sku_outer_ids', str, ['optional']),
                ('postage_id', str, ['optional']),
                ('props', str, ['optional']),
                ('stuff_status', str, ['optional']),
                ('lang', str, ['optional']),
                {'location':[
                    ('state', str, ['optional']),
                    ('city', str, ['optional']),
                    ],
                },
            ],
            # 添加一个商品信息
            'add':[
                {'apiType': 2},
                ('cid', str, []),
                ('props', str, []),
                ('num', str, []),
                ('price', str, []),
                ('type', str, []), # 发布类型,fixed(一口价),auction(拍卖)
                ('stuff_status', str, []), # 新旧程度,new(新),second(二手),unused(闲置)
                ('title', str, []),
                ('desc', str, []),
                {'location':[
                    ('state', str, []),
                    ('city', str, []),
                    ],
                },
                ('valid_thru', str, ['optional']),
                ('has_invoice', str, ['optional']),
                ('has_warranty', str, ['optional']),
                ('seller_cids', str, ['optional']),
                ('has_discount', str, ['optional']),
                ('post_fee', str, ['optional']),
                ('express_fee', str, ['optional']),
                ('list_time', str, ['optional']),
                ('increment', str, ['optional']),
                ('auto_repost', str, ['optional']),
                ('has_showcase', str, ['optional']),
                ('ems_fee', str, ['optional']),
                ('image', str, ['optional']),
                ('format', str, ['optional']),
                ('postage_id', str, ['optional']),
                ('auction_point', str, ['optional']),
                ('property_alias', str, ['optional']),
                ('sku_properties', str, ['optional']),
                ('sku_quantities', str, ['optional']),
                ('sku_prices', str, ['optional']),
                ('sku_outer_ids', str, ['optional']),
                ('lang', str, ['optional']),
                ('freight_payer', str, ['optional']),
                ('approve_status', str, ['optional']), # 商品上传后的状态。onsale出售中，instock库中
            ],
            'update':{
                # 用于商品下架
                'delisting':[
                    {'apiType': 2},
                    ('iid', str, []),
                ],
                # 用以一口价商品的上架
                'listing':[
                    {'apiType': 2},
                    ('iid', str, []),
                    ('num', str, []),
                ],
                # 用于取消商品的橱窗推荐
                'revokeShowcase':[
                    {'apiType': 2},
                    ('iid', str, []),
                ],
                # 用于商品加入橱窗推荐
                'showcase':[
                    {'apiType': 2},
                    ('iid', str, []),
                ],
            },
            'img':{
                # 删除商品图片
                'delete':[
                    {'apiType': 2},
                    ('itemimg_id', str, []),
                    ('iid', str, []),
                ],
                # 对指定商品添加一个商品图片
                'upload':[
                    {'apiType': 2},
                    ('iid', str, []),
                    ('position', str, ['optional']),
                    ('image', str, ['optional']), #  类型 byte[]
                    ('itemimg_id', str, ['optional']),
                    ('is_major', str, ['optional']),
                ],
            },
            'propimg':{
                # 删除属性图片
                'delete':[
                    {'apiType': 2},
                    ('itemimg_id', str, []),
                    ('iid', str, []),
                ],
                # 对商品添加属性图片
                'upload':[
                    {'apiType': 2},
                    ('iid', str, []),
                    ('properties', str, []), # 属性图片属性串pid:vid;pid1:vid1
                    ('propimg_id', str, ['optional']), 
                    ('position', str, ['optional']),
                    ('image', str, ['optional']), #  类型 byte[]
                ],
            },
# todo
#            'sku':{
#                'get':[
#                ],
#                'add':[
#                ],
#                'update':[
#                ],
#            },
#            'postages':{
#                'get':[
#                ],
#            },
        },
# todo
        # " 物流API None "
        'shippings':{
        },
        # " 用户API "
        'user':{
            # 得到单个用户资料。
            'get':[
                {'apiType': 3},
                ('fields', list, []),
                ('nick', str, []),
            ],
        },
        'users': {
            # 批量得到用户公开资料
            'get': [
                {'apiType': 1},
                ('nicks', list, []),
                ('fields', list, [('default', ['nick', 'sex', 'buyer_credit', 'seller_credit'])]),
            ],
        },
    },
    # """ AliSoft """
    'alisoft':{
        # 用户类接口
        # 验证用户身份，确保用户已经登录软件互联平台，并且是该应用的有效使用用户，一般从阿里软件互联平台跳转到ISV应用的身份验证过程中需要使用到。
        'validateUser': [
            {'apiType': 1},
            ('appInstanceId', str, []),
            ('userId', str, []),
            ('token', str, []),
        ],
        # 验证某个用户是否某个订阅实例的有效用户。
        'validateAppUser': [
            {'apiType': 1},
            ('appInstanceId', str, []),
            ('userId', str, []),
        ],
        # 验证群软件用户身份，确保用户已经登录软件互联平台，并且是该订购所在群的有效用户，一般从群服务tab页跳转到ISV应用的身份验证过程中需要使用到。
        'validateTribeUser': [
            {'apiType': 1},
            ('appInstanceId', str, []),
            ('userId', str, []),
            ('token', str, []),
        ],
        'udb':{
            # 根据阿里巴巴中文站（或淘宝）帐号，获取用户在软件互联平台的用户ID。
            'getUserId': [
                {'apiType': 1},
                ('domainid', str, []),
                ('loginid', str, []),
            ],
            # 根据软件互联平台的用户ID，获取用户的阿里巴巴中文站（或淘宝）帐号。
            'getDomainLoginId': [
                {'apiType': 1},
                ('domainid', str, ['default','3']),
                ('userid', str, []),
            ],
        },
        # 订购类接口
        # 获取某个用户订购的应用的用户列表。（没用的功能，只返回自己）
        'getUsingUser': [
            {'apiType': 1},
            ('appInstanceId', str, []),
        ],
        # Isv查询订阅记录的功能。
        'getSubscCtrl': [
            {'apiType': 1},
            ('appInstanceId', str, []),
        ],
        # 旺旺类接口 
        # 2009.4.13 目前都不能使用！！！
        'im':{
            # 获取单个用户阿里旺旺在线的状态
            'userstatus2': [
                ('uid', str, []),
                ('site', str, ['default', 'wangwang']), # 网站前缀包括: 阿里巴巴中文站--cnalichn  淘宝网--cntaobao 旺号--wangwang
                ('v', str, ['default', '6']),
            ],
            # 获取取阿里旺旺用户在线状态图标
            'online': [
                ('uid', str, []),
                {'site': 'wangwang'},
                {'s': 1},
                {'v': 6},
            ],
            # 返回阿里旺旺用户洽谈JS脚本，在页面中嵌入此脚本，就能够自动调出旺旺的聊天窗口
            'msg': [
                ('uid', str, []),
                ('site', str, []),
                ('v', int, [('default', 6)]),
            ],
            # 获取多个用户ID阿里旺旺在线状态
            'mullidstatus': [
                ('beginnum', str, []),
                ('uids', str, []),
                ('v', str, [('default', '6')]),
            ],
#            'nofityMessage': [
#                ('user_Id', str, []),
#                ('app_Instance_Id', str, []),
#                ('message', str, []),
#            ],
        },
    },
}

class Namespace(object):
    def __init__(self, name, client=None):
        self._name = name
        self._client = client
        self._parent = None
        self._children = []
        self._proxy = None

    def fullName(self):
        if self._parent is None:
            return self._name
        else:
            return '%s.%s' % (self._parent.fullName(), self._name)

    def title(self):
        if self._parent is None:
            return self._name.title()
        else:
            return '%s%s' % (self._parent.title(), self._name.title())
        
    def addChild(self, child):
        child._parent = self
        self._children.append(child)
        self.__dict__[child._name] = child

    def createProxy(self, client):
        if self._proxy is None:
            return Namespace(self._name)
        else:
            return eval('%s(self._name, client)' % (self._proxy.__name__))
            
    def __str__(self):
        return self.fullName()

    def __repr__(self):
        return '%s' % (self.fullName())
        
    def __call__(self, method=None, args=None):
        if method is None:
            return self
        return self._client('%s.%s' % (self.fullName(), method), args)
    

def __generate_namespace(namespace, parent=None):
    current = Namespace(namespace[0])

    if parent is not None:
        parent.addChild(current)
        
    methods = {}
    
    for child in namespace[1].items():
        if type(child[1]) == dict:
            __generate_namespace(child, current)
        elif type(child[1]) == list:
            params = ['self']
            body = ['args = {}']

            for parameter in child[1]:

                if isinstance(parameter, tuple):
                    param_name = parameter[0]
                    param_type = parameter[1]
                    param_options = parameter[2]
                    
                    method = child[0]
                    param = param_name

                    for option in param_options:
                        if isinstance(option, tuple) and option[0] == 'default':
                            if param_type == list:
                                param = '%s=None' % param_name
                                body.append('if %s is None: %s = %s' % (param_name, param_name, repr(option[1])))
                            else:
                                param = '%s=%s' % (param_name, repr(option[1]))

                    if 'optional' in param_options:
                        param = '%s=None' % param_name
                        body.append('if %s is not None: args[\'%s\'] = %s' % (param_name, param_name, param_name))
                    else:
                        body.append('args[\'%s\'] = %s' % (param_name, param_name))

                    params.append(param)
                
            body.insert(0, '"""method"""')
            body.insert(0, 'def %s(%s):' % (method, ', '.join(params)))
            body.append('return self(\'%s\', args)' % method)

            exec('\n    '.join(body))
            methods[method] = eval(method)

    if len(methods) > 0:
        proxy = type('%sProxy' % (current.title()), (Namespace, ), methods)
        current._proxy = proxy
        globals()[proxy.__name__] = proxy
    
    return current

def __generate_namespaces():
    for namespace in SIPMETHODS.items():
        current = __generate_namespace(namespace)
        NAMESPACES.append(current)

__generate_namespaces()


class Sip(object):

    def __init__(self, app_key, cert_code, sip_sessionid, sip_url=None,):# sip_redirecturl=None, auth_token=None, app_name=None, internal=None, proxy=None,
        self.app_key = app_key
        self.cert_code = cert_code
        self.sip_sessionid = sip_sessionid
        self.appInstanceId = None # alisoft user method need it

#        self.uid = None # userId and userid
#        self.auth_token = auth_token
#        self.secret = None
#        self.sip_redirecturl = sip_redirecturl
#             
#        self.session_key = None
#        self.session_key_expires = None
#        self.page_id = None
#        self.app_name = app_name
#        self.internal = internal
#        self.proxy = proxy
        
        if sip_url is None:
            self.sip_url = SIP_REST_URL
        elif sip_url == 'server':
            self.sip_url = SIP_SERVICE_URL
        else:
            self.sip_url = sip_url
        
        " Keys 目前只支持淘宝 "
#        self.key_list={
#            'query':['sip_appkey', 'sip_apiname', 'sip_sessionid', 'sip_service_command'],
#            'unbind':['sip_appkey', 'sip_apiname', 'sip_sessionid', 'sip_service_command'],
#            'isLogin':['sip_appkey', 'sip_apiname', 'sip_sessionid', 'sip_service_command','sip_tokentype' ],
#            'binduser':['sip_apiname', 'sip_appkey', 'sip_sessionid', 'sip_redirecturl'],
#            }
        self.key_dict= {
            'query':{
                'sip_appkey':app_key,
                'sip_apiname':'taobao.shop.update',
                'sip_sessionid':sip_sessionid,
                'sip_service_command':'sip.user.query',
                },
            'unbind':{
                'sip_appkey':app_key,
                'sip_apiname':'taobao.shop.update',
                'sip_sessionid':sip_sessionid,
                'sip_service_command':'sip.user.unbind',
                },
            'isLogin':{    
                'sip_appkey':app_key,
                'sip_apiname':'taobao.shop.update',
                'sip_sessionid':sip_sessionid,
                'sip_service_command':'sip.user.isLogin',
                'sip_tokentype':'session',
                },
            'login':{
                'sip_appkey':app_key,
                'sip_sessionid':sip_sessionid,
                'sip_apiname':'taobao.shop.update',
#                'sip_redirecturl': ???
                },
            }
        

        self._create_proxies()

    def _create_proxy(self, namespace, parent=None):
        proxy = namespace.createProxy(self)
        if parent is None:
            self.__dict__[namespace._name] = proxy
        else:
            parent.addChild(proxy)
        
        for child in namespace._children:
            self._create_proxy(child, proxy)
            
    def _create_proxies(self):
        for namespace in NAMESPACES:
            self._create_proxy(namespace)
            
    def _sign(self, args):
        encrypt = md5.new()
        encrypt.update(self.cert_code)
        for k, v in sorted(args.items()):
            encrypt.update(k)
            # todo 临时解决 传int类型的问题
            v = str(v)
            encrypt.update(v)
        return encrypt.hexdigest().upper()

    def _build_post_args(self, method, args=None):
        """adds to args parameters that are necessary for every call to the Sip."""
        if args is None:
            args = {}

        for arg in args.items():
            if type(arg[1]) == list:
                args[arg[0]] = ','.join(str(a) for a in arg[1])
            elif type(arg[1]) == unicode:
                args[arg[0]] = arg[1].encode("UTF-8")
            elif type(arg[1]) == bool:
                args[arg[0]] = str(arg[1]).lower()

        args['sip_appkey'] = self.app_key #ok
        args['sip_apiname'] = method #ok
        args['v'] = '1.0' #ok
        args['format'] = RESPONSE_FORMAT #ok
        args['sip_timestamp'] = self.get_timestamp() #ok
        args['sip_sessionid'] = self.sip_sessionid #ok
        args['sip_version'] = '1.0' #ok update：20090428
        args['sip_sign'] = self._sign(args) #ok

        return args

    def _parse_rsp(self, response, method, format=None):
        " 分析返回值 "
        if not format:
            format = RESPONSE_FORMAT

        if format == 'json':
            # 在这里经常淘宝没有返回数据 P36，引发 ValueError: NO JSON ....的情况，需要处理！
            result = simplejson.loads(response)

        elif format == 'xml':
            dom = minidom.parseString(response)
            result = self._parse_rsp_item(dom)
            dom.unlink()

        else:
            raise RuntimeError('Invalid format specified.')
            
        return result

    def _parse_rsp_item(self, node):
        """Parses an XML response node from Sip."""
        if node.nodeType == node.DOCUMENT_NODE and \
            node.childNodes[0].hasAttributes() and \
            node.childNodes[0].hasAttribute('list') and \
            node.childNodes[0].getAttribute('list') == "true":
            return {node.childNodes[0].nodeName: self._parse_rsp_list(node.childNodes[0])}
        elif node.nodeType == node.ELEMENT_NODE and \
            node.hasAttributes() and \
            node.hasAttribute('list') and \
            node.getAttribute('list')=="true":
            return self._parse_rsp_list(node)
        elif len(filter(lambda x: x.nodeType == x.ELEMENT_NODE, node.childNodes)) > 0:
            return self._parse_rsp_dict(node)
        else:
            return ''.join(node.data for node in node.childNodes if node.nodeType == node.TEXT_NODE)
     
    def _parse_rsp_dict(self, node):
        """Parses an XML dictionary response node from Sip."""
        result = {}
        for item in filter(lambda x: x.nodeType == x.ELEMENT_NODE, node.childNodes):
           result[item.nodeName] = self._parse_rsp_item(item)
        if node.nodeType == node.ELEMENT_NODE and node.hasAttributes():
           if node.hasAttribute('id'):
               result['id'] = node.getAttribute('id')
        return result

    def _parse_rsp_list(self, node):
        """Parses an XML list response node from Sip."""
        result = []
        for item in filter(lambda x: x.nodeType == x.ELEMENT_NODE, node.childNodes):
            result.append(self._parse_rsp_item(item))
        return result

    def get_timestamp(self):
        stime = None
        if(time.timezone == 0):
            gmtimefix = 28800
            stime = time.gmtime(gmtimefix + time.time())
        else:
            stime = time.localtime()
        strtime = time.strftime(ISOTIMEFORMAT, stime)
        return strtime
    
    def unicode_urlencode(self, params):
        if isinstance(params, dict):
            params = params.items()
        return urllib.urlencode([(k, isinstance(v, unicode) and v.encode('UTF-8') or v) for k, v in params])
    
    def __call__(self, method=None, args=None):
        post_data = self.unicode_urlencode(self._build_post_args(method, args))

        response = urlread(self.sip_url, post_data)

        return self._parse_rsp(response, method)

    
    def get_url(self, key, field, need_sign=True):
        """ 根据限定的关键词和限定的作用域（login or server）拼合出合适的链接 """
        def _create_sign(keys,content):
            """ Create sign. """
            each_key = []
            for i in keys:
                each_key.append(i)
            each_key.sort()

            sign = ''
            for i in each_key:
                sign += i
                sign += str(content[i])
            sign = self.cert_code + sign

            encrypt = md5.new()
            encrypt.update(sign)

            return encrypt.hexdigest().upper()
        
        def _create_url(keys,content):
            """ Create sip url only , Not sign. """
            url = ''
            for i in keys:
                url += '%s=%s&' % (i, str(content[i]))
            return url

        prefix = SIP_REST_URL
        if field == 'login':
            prefix = SIP_LOGIN_URL
        elif field == 'server':
            prefix = SIP_SERVICE_URL

        keys = self.key_dict[key].keys()
        content = self.key_dict[key]

        url = prefix + '?' + _create_url(keys, content)
        if need_sign:
            sign = _create_sign(keys, content)
            url = url + 'sip_sign=' + sign
        return url

    def check_session(self, key='isLogin'):
        """ Check ... """
        url = self.get_url(key, 'server')
        response = urlread(url, all=True)
        status = response.headers['sip_status']
        if status == '9999':
            return True
        elif status == '1004':
            return False
        else:
            raise 'SipError', 'response starus error, not 9999 or 1004'
        
    def login(self, popup=False):
        """Open a web browser telling the user to login to Sip."""
        import webbrowser
        webbrowser.open(self.get_login_url(popup=popup))
    
    def get_item_url(self, iid):
        "获取商品在淘宝上的链接"
        return TAOBAO_ITEM_URL+iid+'.htm'

def logger(name, value):
    " logger(name, value) "
    import logging
    logging.warning(name+' : '+ str(value))


if __name__ == '__main__':
    APPKEY = '25901'
    CERTCODE = '6e1ce7a030dc11dea751cefd64740030'
    SIPSESSIONID = "987654321"
    MY_ALISOFT_URL = 'http://mall.alisoft.com/apps/shopwindow/showAppDetailAction!view.jspa?appID='+APPKEY
    
    sip = Sip(app_key=APPKEY, cert_code=CERTCODE, sip_sessionid=SIPSESSIONID)
    